package cn.com.connext.scrm.service;

import cn.com.connext.scrm.domain.master.*;
import cn.com.connext.scrm.repository.master.*;
import cn.com.connext.scrm.service.dto.*;
import cn.com.connext.scrm.service.mapper.*;
import com.alibaba.fastjson.JSON;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 2 * @Author: shawn
 * 3 * @Date: 2019/11/5 10:23 上午
 * 4
 */
@Service
@Transactional
public class QuickSelectWine4IpadService {

    private final Logger log = LoggerFactory.getLogger(QuickSelectWineService.class);

    @Autowired
    private QuickSelectProductIpadRepository quickSelectProductIpadRepository;

    @Autowired
    private QuickSelectProductTagIpadRepository quickSelectProductTagIpadRepository;

    @Autowired
    private QuickSelectRecordIpadRepository quickSelectRecordIpadRepository;

    @Autowired
    private QuickSelectRecordIpadMapper quickSelectRecordIpadMapper;

    @Autowired
    private QuickSelectProductIpadMapper quickSelectProductIpadMapper;

    @Autowired
    private QuickSelectProductTagIpadMapper quickSelectProductTagIpadMapper;

    //region 选酒记录相关方法

    /**
     * 根据用户unionid获取选酒记录
     *
     * @param pageable
     * @param unionId
     * @return
     */
    public Page<QuickSelectRecordIpadDTO> getAllRecords(Pageable pageable, String unionId) {
        Page<QuickSelectRecordIpadDTO> result = quickSelectRecordIpadRepository.findAll(getRecordWhereClause(unionId), pageable);
        return result;
    }

    /**
     * 拼接查询条件
     *
     * @param unionId
     * @return
     */
    private Specification<QuickSelectRecordIpad> getRecordWhereClause(String unionId) {
        Specification<QuickSelectRecordIpad> specification = (Root<QuickSelectRecordIpad> root, CriteriaQuery<?> query,
                                                          CriteriaBuilder cb) -> {
            List<Predicate> predicates = new ArrayList<>();

            if (StringUtils.isNotEmpty(unionId)) {
                Predicate lineFactoryLike_add = cb.equal(root.get("unionId"), unionId);
                predicates.add(lineFactoryLike_add);
            }
            Predicate state_add = cb.equal(root.get("saveByUser"), true);
            predicates.add(state_add);
            query.orderBy(cb.desc(root.get("createdTime")/*.as(ZonedDateTime.class)*/));
            Predicate[] ps = new Predicate[predicates.size()];
            return cb.and(predicates.toArray(ps));
        };
        return specification;
    }

    /**
     * 根据Id获取选酒记录
     *
     * @param id
     * @return
     */
    public QuickSelectRecordIpadDTO getRecordById(String id) {
        QuickSelectRecordIpad result = quickSelectRecordIpadRepository.getOne(id);
        QuickSelectRecordIpadDTO dto = quickSelectRecordIpadMapper.toDto(result);
        if (dto != null) {
            List<QuickSelectProductIpadDTO> list = JSON.parseArray(dto.getSelectResults(), QuickSelectProductIpadDTO.class);
            for (int i = 0; i < list.size(); i++) {
                QuickSelectProductIpadDTO item = list.get(i);

                List<String> genderList = new ArrayList<>();
                List<String> ageList = new ArrayList<>();
                List<String> frenquencyList = new ArrayList<>();
                List<String> ocassionList = new ArrayList<>();
                List<String> flavorList = new ArrayList<>();

                List<QuickSelectProductTagIpad> tags = quickSelectProductTagIpadRepository.queryByWId(item.getwId());
                for (int j = 0; j < tags.size(); j++) {
                    QuickSelectProductTagIpad tag = tags.get(j);
                    switch (tag.getCategory()) {
                        case "gender":
                            genderList.add(tag.getName());
                            break;
                        case "age":
                            ageList.add(tag.getName());
                            break;
                        case "frequency":
                            frenquencyList.add(tag.getName());
                            break;
                        case "ocassion":
                            ocassionList.add(tag.getName());
                            break;
                        case "flavor":
                            flavorList.add(tag.getName());
                            break;

                    }
                }
                item.setAgeList(genderList);
                item.setGenderList(ageList);
                item.setFrequencyList(frenquencyList);
                item.setOcassionList(ocassionList);
                item.setFlavorList(flavorList);
            }
            dto.setSelectResults(JSON.toJSONString(list));
        }
        return dto;
    }

    /**
     * 保存选酒记录
     *
     * @param dto
     * @return
     */
    public QuickSelectRecordIpadDTO saveSelectRecord(QuickSelectRecordIpadDTO dto) {
        dto.setCreatedTime(new Date());
        QuickSelectRecordIpad entity = quickSelectRecordIpadMapper.toEntity(dto);
        entity = quickSelectRecordIpadRepository.save(entity);
        return quickSelectRecordIpadMapper.toDto(entity);
    }

    /**
     * 修改瓶酒记录保存状态
     *
     * @param id
     * @return
     */
    public QuickSelectRecordIpadDTO updateRecordSaveState(String id) {
        QuickSelectRecordIpad entity = quickSelectRecordIpadRepository.getOne(id);
//        if (entitys.size() < 1) {
//            log.error("updateRecordSaveState数据不存在:{}", id);
//            throw new ServiceException("数据不存在");
//        }
//        QuickSelectRecord entity = entitys.get(0);
        entity.setUpdatedTime(new Date());
        entity.setSaveByUser(true);
        entity = quickSelectRecordIpadRepository.save(entity);
        return quickSelectRecordIpadMapper.toDto(entity);
    }
    //endregion

    // region 选酒推荐

    /**
     * 根据所选标签计算推荐酒款
     *
     * @param unionId
     * @param quickSelectParaIpadDTO
     * @return
     */
    public QuickSelectRecordIpadDTO getRecommendWine(String unionId, QuickSelectParaIpadDTO quickSelectParaIpadDTO) {
        List<QuickSelectProductIpadDTO> wines = getRecommendLogic2(quickSelectParaIpadDTO);

        QuickSelectRecordIpad record = new QuickSelectRecordIpad();
        record.setUnionId(unionId);
        record.setSelectTags(JSON.toJSONString(quickSelectParaIpadDTO));
        record.setSelectResults(JSON.toJSONString(wines));
        record.setCreatedTime(new Date());
        record.setUpdatedTime(new Date());
        record.setSaveByUser(false);
        record = quickSelectRecordIpadRepository.save(record);

        QuickSelectRecordIpadDTO dto = this.getRecordById(record.getId());
        return dto;
    }

    private List<QuickSelectProductIpadDTO> getRecommendLogic(QuickSelectParaIpadDTO quickSelectParaIpadDTO) {
        List<QuickSelectTagIpadDTO> flavors = quickSelectParaIpadDTO.getSelectFlavorList();

        List<String> codes = new ArrayList<>();
        flavors.forEach(o -> {
            if (!"subCategory".equals(o.getSubCategory())) {
                codes.add(o.getCode());
            }
        });

        //region 根据标签筛选出数据库中匹配标签数据
        List<QuickSelectProductTagIpad> list = quickSelectProductTagIpadRepository.getTagsByCodes(codes);
        log.info("getRecommendWine.list:{}", list.toString());
        Map<String, QuickSelectWine4IpadService.TagMatchInfo> map = new HashMap<String, QuickSelectWine4IpadService.TagMatchInfo>();
        for (int i = 0; i < list.size(); i++) {
            QuickSelectProductTagIpad tag = list.get(i);
            QuickSelectWine4IpadService.TagMatchInfo info;
            if (map.containsKey(tag.getwId())) {
                info = map.get(tag.getwId());
            } else {
                info = new QuickSelectWine4IpadService.TagMatchInfo();
                info.setwId(tag.getwId());
                map.put(tag.getwId(), info);
            }

            switch (tag.getCategory()) {
                case "flavor":
                    info.setFlavorSum(info.getFlavorSum() + tag.getWeight());
                    info.setFlavorCount(info.getFlavorCount() + 1);
                    break;
                case "ocassion":
                    if (quickSelectParaIpadDTO.getOcassion().equals(tag.getCode()))
                        info.setOcassionSum(tag.getWeight());
                    break;
                case "age":
                    if (quickSelectParaIpadDTO.getAge().equals(tag.getCode()))
                        info.setAgeSum(tag.getWeight());
                    break;
                case "gender":
                    if (quickSelectParaIpadDTO.getGender().equals(tag.getCode()))
                        info.setGenderSum(tag.getWeight());
                    break;
                case "frequency":
                    if (quickSelectParaIpadDTO.getFrequency().equals(tag.getCode()))
                        info.setFrequencySum(tag.getWeight());
                    break;
                default:
                    break;
            }
        }

        log.info("getRecommendWine.map:{}", map.toString());

        int count = quickSelectParaIpadDTO.getCount();
        List<QuickSelectWine4IpadService.TagMatchInfo> matchList = map.values().stream().filter(o -> o.getFlavorCount() >= flavors.size()).collect(Collectors.toList());
        log.info("getRecommendWine.tempList1:{}", matchList.toString());
        if (matchList.size() > count) {
            List<QuickSelectWine4IpadService.TagMatchInfo> tempList = matchList.stream().filter(o -> o.getOcassionSum() > 0).collect(Collectors.toList());
            log.info("getRecommendWine.tempList2:{}", tempList.toString());
            if (tempList.size() > 0) {
                matchList = tempList;
            }
        }
        if (matchList.size() > count) {
            List<QuickSelectWine4IpadService.TagMatchInfo> tempList = matchList.stream().filter(o -> o.getAgeSum() > 0).collect(Collectors.toList());
            log.info("getRecommendWine.tempList3:{}", tempList.toString());
            if (tempList.size() > 0) {
                matchList = tempList;
            }
        }
        if (matchList.size() > count) {
            List<QuickSelectWine4IpadService.TagMatchInfo> tempList = matchList.stream().filter(o -> o.getGenderSum() > 0).collect(Collectors.toList());
            log.info("getRecommendWine.tempList4:{}", tempList.toString());
            if (tempList.size() > 0) {
                matchList = tempList;
            }
        }
        if (matchList.size() > count) {
            List<QuickSelectWine4IpadService.TagMatchInfo> tempList = matchList.stream().filter(o -> o.getFrequencySum() > 0).collect(Collectors.toList());
            log.info("getRecommendWine.tempList5:{}", tempList.toString());
            if (tempList.size() > 0) {
                matchList = tempList;
            }
        }

//        matchList.sort(Comparator.naturalOrder());
        Collections.sort(matchList, new Comparator<QuickSelectWine4IpadService.TagMatchInfo>() {
            @Override
            public int compare(QuickSelectWine4IpadService.TagMatchInfo o1, QuickSelectWine4IpadService.TagMatchInfo o2) {
                if (o1.getFlavorSum() > o2.getFlavorSum()) {
                    return 1;
                } else {
                    return -1;
                }
//                return 0;
            }
        });
        List<String> productIds = new ArrayList<>();
        for (int j = 0; j < count && j < matchList.size(); j++) {
            productIds.add(matchList.get(j).getwId());
        }

        List<QuickSelectProductIpadDTO> result2 = new ArrayList<>();
        if (productIds.size() > 0) {
            List<QuickSelectProductIpad> pList = quickSelectProductIpadRepository.getAllByWId(productIds);
            result2 = quickSelectProductIpadMapper.toDto(pList);
        }
        return result2;
//        private List<String> getRecommendProductIds(int count, )


        //endregion
    }

    private List<QuickSelectProductIpadDTO> getRecommendLogic2(QuickSelectParaIpadDTO quickSelectParaIpadDTO) {
        log.info("getRecommendLogic2.quickSelectParaDTO:{}", JSON.toJSONString(quickSelectParaIpadDTO));
        List<QuickSelectTagIpadDTO> flavors = quickSelectParaIpadDTO.getSelectFlavorList();

        List<String> codes = new ArrayList<>();
        flavors.forEach(o -> {
            if (!"subCategory".equals(o.getSubCategory())) {
                codes.add(o.getCode());
            }
        });
        if (StringUtils.isNotEmpty(quickSelectParaIpadDTO.getOcassion())) {
            codes.add(quickSelectParaIpadDTO.getOcassion());
        }
        if (StringUtils.isNotEmpty(quickSelectParaIpadDTO.getAge())) {
            codes.add(quickSelectParaIpadDTO.getAge());
        }
        if (StringUtils.isNotEmpty(quickSelectParaIpadDTO.getGender())) {
            codes.add(quickSelectParaIpadDTO.getGender());
        }
        if (StringUtils.isNotEmpty(quickSelectParaIpadDTO.getFrequency())) {
            codes.add(quickSelectParaIpadDTO.getFrequency());
        }
        log.info("getRecommendLogic2.codes:{}", JSON.toJSONString(codes));

        //region 根据标签筛选出数据库中匹配标签数据
        List<QuickSelectProductTagIpad> list = quickSelectProductTagIpadRepository.getTagsByCodes(codes);
        log.info("getRecommendLogic2.tags:{}", JSON.toJSONString(list));
        Map<String, QuickSelectWine4IpadService.TagMatchInfo> map = new HashMap<String, QuickSelectWine4IpadService.TagMatchInfo>();
        for (int i = 0; i < list.size(); i++) {
            QuickSelectProductTagIpad tag = list.get(i);
            QuickSelectWine4IpadService.TagMatchInfo info;
            if (map.containsKey(tag.getwId())) {
                info = map.get(tag.getwId());
            } else {
                info = new QuickSelectWine4IpadService.TagMatchInfo();
                info.setwId(tag.getwId());
                map.put(tag.getwId(), info);
            }

            switch (tag.getCategory()) {
                case "flavor":
                    info.setFlavorSum(info.getFlavorSum() + tag.getWeight());
                    info.setFlavorCount(info.getFlavorCount() + 1);
                    break;
                case "ocassion":
                    if (quickSelectParaIpadDTO.getOcassion().equals(tag.getCode())) {
                        info.setFlavorSum(info.getFlavorSum() + tag.getWeight());
                        info.setOcassionSum(tag.getWeight());
                    }
                    break;
                case "age":
                    if (quickSelectParaIpadDTO.getAge().equals(tag.getCode())) {
                        info.setFlavorSum(info.getFlavorSum() + tag.getWeight());
                        info.setAgeSum(tag.getWeight());
                    }
                    break;
                case "gender":
                    if (quickSelectParaIpadDTO.getGender().equals(tag.getCode())) {
                        info.setFlavorSum(info.getFlavorSum() + tag.getWeight());
                        info.setGenderSum(tag.getWeight());
                    }
                    break;
                case "frequency":
                    if (quickSelectParaIpadDTO.getFrequency().equals(tag.getCode())) {
                        info.setFlavorSum(info.getFlavorSum() + tag.getWeight());
                        info.setFrequencySum(tag.getWeight());
                    }
                    break;
                default:
                    break;
            }
        }

        int count = quickSelectParaIpadDTO.getCount();
        List<QuickSelectWine4IpadService.TagMatchInfo> matchList = map.values().stream().collect(Collectors.toList());

        Collections.sort(matchList, new Comparator<QuickSelectWine4IpadService.TagMatchInfo>() {
            @Override
            public int compare(QuickSelectWine4IpadService.TagMatchInfo o1, QuickSelectWine4IpadService.TagMatchInfo o2) {
                if (o1.getFlavorSum() < o2.getFlavorSum()) {
                    return 1;
                } else if (o1.getFlavorSum() > o2.getFlavorSum()) {
                    return -1;
                }
                return 0;
            }
        });
        log.info("getRecommendWine.matchList-afterSort: {}", JSON.toJSONString(matchList));
        List<String> productIds = new ArrayList<>();
        for (int j = 0; j < count && j < matchList.size(); j++) {
            productIds.add(matchList.get(j).getwId());
        }

        List<QuickSelectProductIpadDTO> result2 = new ArrayList<>();
        if (productIds.size() > 0) {
            List<QuickSelectProductIpad> pList = quickSelectProductIpadRepository.getAllByWId(productIds);
            List<QuickSelectProductIpadDTO> resultUnsort = quickSelectProductIpadMapper.toDto(pList);
            productIds.forEach(o -> {
                resultUnsort.forEach(x -> {
                    if (o.equals(x.getwId())) {
                        result2.add(x);
                    }
                });
            });
        }
        log.info("getRecommendWine.result2: {}", JSON.toJSONString(result2));
        return result2;
        //endregion
    }

    public class TagMatchInfo {
        private String wId;
        private int flavorCount;
        private int flavorSum;
        private int genderSum;
        private int ageSum;
        private int frequencySum;
        private int ocassionSum;

        public String getwId() {
            return wId;
        }

        public void setwId(String wId) {
            this.wId = wId;
        }

        public int getFlavorCount() {
            return flavorCount;
        }

        public void setFlavorCount(int flavorCount) {
            this.flavorCount = flavorCount;
        }

        public int getFlavorSum() {
            return flavorSum;
        }

        public void setFlavorSum(int flavorSum) {
            this.flavorSum = flavorSum;
        }

        public int getGenderSum() {
            return genderSum;
        }

        public void setGenderSum(int genderSum) {
            this.genderSum = genderSum;
        }

        public int getAgeSum() {
            return ageSum;
        }

        public void setAgeSum(int ageSum) {
            this.ageSum = ageSum;
        }

        public int getFrequencySum() {
            return frequencySum;
        }

        public void setFrequencySum(int frequencySum) {
            this.frequencySum = frequencySum;
        }

        public int getOcassionSum() {
            return ocassionSum;
        }

        public void setOcassionSum(int ocassionSum) {
            this.ocassionSum = ocassionSum;
        }

//        @Override
//        public int compareTo(TagMatchInfo info) {
//            //自定义比较方法，如果认为此实体本身大则返回1，否则返回-1
//            if (this.getFlavorSum() < info.getFlavorSum()) {
//                return 1;
//            }
//            return -1;
//        }
    }


    //endregion

    //region 选酒标签相关

    /**
     * 获取所有子类类型风味标签
     *
     * @return
     */
    public List<QuickSelectProductTagIpadDTO> getAllSubCategoryFlavors() {
        List<QuickSelectProductTagIpad> list = quickSelectProductTagIpadRepository.queryByCategoryAndSubCategory("flavor", "subCategory");
//        return convertTagDTOList(new ArrayList<>(), list);
        return quickSelectProductTagIpadMapper.toDto(list);
    }

    /**
     * 根据风味标签子类型获取风味标签记录
     *
     * @param subCategory
     * @return
     */
    public List<QuickSelectTagIpadDTO> getFlavorsBySubCategory(String subCategory) {
        List<QuickSelectProductTagIpad> list = quickSelectProductTagIpadRepository.queryByCategoryAndSubCategory("flavor", subCategory);
//        log.info("getAllSubCategoryFlavors.result:{}", list);
        List<QuickSelectProductTagIpad> newList = list.stream().filter(o -> o.getCode().equals(subCategory)).collect(Collectors.toList());

        if (newList.size() > 0) {
            List<QuickSelectProductTagIpad> list2 = new ArrayList<>();
            list2.addAll(newList);
            for (int i = 0; i < list.size(); i++) {
                if (!subCategory.equals(list.get(i).getCode())) {
                    list2.add(list.get(i));
                }
            }
            list = list2;
        }
        return convertTagDTOList(new ArrayList<>(), list);
    }

    /**
     * 根据风味标签子类型获取风味标签记录
     *
     * @param subCategory
     * @return
     */
    public List<QuickSelectTagIpadDTO> getRelevantFlavorsBySubCategory(String subCategory) {
        List<String> codes = new ArrayList<>();
        codes.add(subCategory);
        List<String> wIds = quickSelectProductTagIpadRepository.getWIdsByFlavor(codes, codes.size());
        log.info("getRelevantFlavors.wIds:{}", wIds);
        if (wIds.size() < 1) {
            return new ArrayList<>();
        }
        List<QuickSelectProductTagIpad> list = quickSelectProductTagIpadRepository.getFlavorsByWIds(wIds);
        List<QuickSelectTagIpadDTO> result = convertTagDTOList(new ArrayList<>(), list);

        //把跟子类同名的风味放在最前面
        List<QuickSelectTagIpadDTO> select = result.stream().filter(o -> o.getCode().equals(subCategory)).collect(Collectors.toList());
        result.forEach(o -> {
            if (!o.getCode().equals(subCategory)) select.add(o);
        });
        return select;
    }

    /**
     * 根据已选风味标签获取相关风味标签记录
     *
     * @param tags
     * @return
     */
    public List<QuickSelectTagIpadDTO> getRelevantFlavors(String subCategory, List<QuickSelectTagIpadDTO> tags) {
        List<String> codes = new ArrayList<>();
        tags.forEach(o -> codes.add(o.getCode()));
        List<String> wIds = quickSelectProductTagIpadRepository.getWIdsByFlavor(codes, codes.size());
        log.info("getRelevantFlavors.wIds:{}", wIds);
        if (wIds.size() < 1) {
            return new ArrayList<>();
        }
        List<QuickSelectProductTagIpad> list = quickSelectProductTagIpadRepository.getFlavorsByWIds(wIds);
        return convertTagDTOList(codes, list);
    }

    private List<QuickSelectTagIpadDTO> convertTagDTOList(List<String> existCodes, List<QuickSelectProductTagIpad> list) {
        List<QuickSelectTagIpadDTO> result = new ArrayList<>();
        List<String> elist = new ArrayList<>();
        for (int i = 0; i < list.size(); i++) {
            QuickSelectProductTagIpad tag = list.get(i);
            if (!elist.contains(tag.getCode()) && !existCodes.contains(tag.getCode())) {
                QuickSelectTagIpadDTO dto = new QuickSelectTagIpadDTO();
                dto.setCategory(tag.getCategory());
                dto.setSubCategory(tag.getSubCategory());
                dto.setCode(tag.getCode());
                dto.setName(tag.getName());
                dto.setImageUrl(tag.getImageUrl());
                elist.add(dto.getCode());
                result.add(dto);
            }
        }
        return result;
    }
    //endregion

}
